package com.lagou.filter;

import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 自定义扩展拦截器
 */
@Activate(group = {CommonConstants.CONSUMER})
public class TPMonitorFilter implements Filter {
    private static Map<String, List<MethodInvokeInfo>> methodInvokeMap = new ConcurrentHashMap<>();

    static {
        methodInvokeMap.put("methodA", new ArrayList<>());
        methodInvokeMap.put("methodB", new ArrayList<>());
        methodInvokeMap.put("methodC", new ArrayList<>());
        ScheduledExecutorService executorService = Executors.newScheduledThreadPool(2);
        executorService.scheduleAtFixedRate(new CountThread(), 5000,5000, TimeUnit.MILLISECONDS);
    }

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        System.out.println("123123123123");
        long startTime = System.currentTimeMillis();
        Result result = invoker.invoke(invocation);
        long endTime = System.currentTimeMillis();
        long timeConsuming = endTime - startTime;
        MethodInvokeInfo methodInvokeInfo = new MethodInvokeInfo();
        methodInvokeInfo.setTimeConsuming(timeConsuming);
        methodInvokeInfo.setInvokeTime(endTime);
        if (invocation.getMethodName().indexOf("A") != -1) {
            List<MethodInvokeInfo> methodA = methodInvokeMap.get("methodA");
            methodA.add(methodInvokeInfo);
            methodInvokeMap.put("methodA", methodA);
        } else if (invocation.getMethodName().indexOf("B") != -1) {
            List<MethodInvokeInfo> methodB = methodInvokeMap.get("methodB");
            methodB.add(methodInvokeInfo);
            methodInvokeMap.put("methodB", methodB);
        } else if (invocation.getMethodName().indexOf("C") != -1) {
            List<MethodInvokeInfo> methodC = methodInvokeMap.get("methodC");
            methodC.add(methodInvokeInfo);
            methodInvokeMap.put("methodC", methodC);

        }
        return result;
    }

    static class CountThread implements Runnable {
        @Override
        public void run() {
            // 清理一分钟前的数据
            System.out.println("running.......");
            clearHistoricalData();
            for (Map.Entry<String, List<MethodInvokeInfo>> stringListEntry : methodInvokeMap.entrySet()) {
                List<MethodInvokeInfo> value = stringListEntry.getValue();
                if (value.size() > 0) {
                    int indexTP90 = (int) Math.floor(value.size() * 0.90);
                    int indexTP99 = (int) Math.floor(value.size() * 0.99);
                    long timeConsumingTP90 = value.get(indexTP90).getTimeConsuming();
                    long timeConsumingTP99 = value.get(indexTP99).getTimeConsuming();
                    System.out.println("TP90 =============>> method:" + stringListEntry.getKey() + ",耗时：" + timeConsumingTP90);
                    System.out.println("TP99 =============>> method:" + stringListEntry.getKey() + ",耗时：" + timeConsumingTP99);
                }
            }
        }
    }

    private static void clearHistoricalData() {
        for (Map.Entry<String, List<MethodInvokeInfo>> stringListEntry : methodInvokeMap.entrySet()) {
            List<MethodInvokeInfo> value = stringListEntry.getValue();
            for (int i = 0; i < value.size(); i++) {
                MethodInvokeInfo methodInvokeInfo = value.get(i);
                if ((System.currentTimeMillis() - methodInvokeInfo.getInvokeTime()) > 60000) {
                    value.remove(i);
                }
            }
            methodInvokeMap.put(stringListEntry.getKey(), value);
        }
    }

    class MethodInvokeInfo {
        private long timeConsuming;// 耗时
        private long invokeTime;// 执行时间戳

        public long getTimeConsuming() {
            return timeConsuming;
        }

        public void setTimeConsuming(long timeConsuming) {
            this.timeConsuming = timeConsuming;
        }

        public long getInvokeTime() {
            return invokeTime;
        }

        public void setInvokeTime(long invokeTime) {
            this.invokeTime = invokeTime;
        }
    }

}
